/*
 * Copyrignt (c) qkier.com. All Rights Reserved.
 * Author: xuzeshui@qkier.com
 * Created Time: 2017-04-21 14:01:30
 * Last Modified: 2017-04-21 17:21:31
 * File Name: geo/geoaddr/geoaddr.go
 * Description:
 */

//
package geoaddr

import (
	"fmt"
	"time"

	"openapi/db"
	"openapi/geo/bdlbs"
	"openapi/geo/geohash"

	"doubimeizhi.com/datatable"
	"doubimeizhi.com/utility"

	log "github.com/cihub/seelog"
)

var (
	geoaddrData *utility.SafeMap
)

type GeoHashAddrItem struct {
	Hash       string `json:"geohash"`
	LatRange   string `json:"lat_range"`
	LngRange   string `json:"lng_range"`
	AddrFormat string `json:"addr_format"`
	RegionCode string `json:"region_code"`
	Country    string `json:"country"`
	WGS84Coord string `json:"wgs84_coord"`
	BD09Coord  string `json:"bd09_coord"`
}

func QueryGeoAddr(lat, lng float64, coordType string) *GeoHashAddrItem {
	//计算geohash值,精度固定为9位
	hash, box := geohash.Encode(lat, lng, 9)

	var item *GeoHashAddrItem

	if coordType != "wgs84ll" { //非大地坐标系,直接从百度查询,不缓存
		item = bdlbsGetGeoHashAddrItem(lat, lng, coordType)
		if item != nil {
			item.Hash = hash
			item.LatRange = fmt.Sprintf("%.12f,%.12f", box.MinLat, box.MaxLat)
			item.LngRange = fmt.Sprintf("%.12f,%.12f", box.MinLng, box.MaxLng)
		}
		return item
	}

	if val, ok := geoaddrData.Get(hash); ok { //缓存命中
		item = val.(*GeoHashAddrItem)
		return item
	}

	//缓存无法命中
	item = bdlbsGetGeoHashAddrItem(lat, lng, coordType)
	if item == nil { //查询失败
		return nil
	}

	item.WGS84Coord = fmt.Sprintf("%.12f,%.12f", lng, lat)
	item.Hash = hash
	item.LatRange = fmt.Sprintf("%.12f,%.12f", box.MinLat, box.MaxLat)
	item.LngRange = fmt.Sprintf("%.12f,%.12f", box.MinLng, box.MaxLng)

	if err := saveGeoAddrItem2Table(item); err == nil {
		geoaddrData.Set(hash, item)
	}
	return item
}

//向百度lbs查询坐标位置信息
func bdlbsGetGeoHashAddrItem(lat, lng float64, coordType string) *GeoHashAddrItem {
	loc2AddrReq := &bdlbs.GeoLocation2AddrReq{}
	loc2AddrReq.Lat = lat
	loc2AddrReq.Lng = lng
	loc2AddrReq.CoordType = coordType

	loc2AddrResp := bdlbs.NewGeoLocation2AddrResp()
	if err := bdlbs.GetBDLBSService().Location2Addr(loc2AddrReq, loc2AddrResp); err == nil {
		if loc2AddrResp.Code == 0 {
			item := &GeoHashAddrItem{}
			item.AddrFormat = loc2AddrResp.Result.FormattedAddr
			item.RegionCode = loc2AddrResp.Result.AddrComponent.Adcode
			item.Country = fmt.Sprintf("%d:%s", loc2AddrResp.Result.AddrComponent.CountryCode, loc2AddrResp.Result.AddrComponent.Country)
			item.BD09Coord = fmt.Sprintf("%.12f,%.12f", loc2AddrResp.Result.Location.Lng, loc2AddrResp.Result.Location.Lat)
			return item
		} else {
			log.Warnf("Location2Addr Failed status: %d, message: %s", loc2AddrResp.Code, loc2AddrResp.Msg)
		}
	} else {
		log.Warnf("Location2Addr Error: %s", err.Error())
	}
	return nil
}

//将结果写回到数据库表中
func saveGeoAddrItem2Table(item *GeoHashAddrItem) error {
	dti := datatable.NewDataTableInsert()
	dti.SetTableName("geohash_address")
	dti.SetInsertValue("geohash", item.Hash)
	dti.SetInsertValue("lat_range", item.LatRange)
	dti.SetInsertValue("lng_range", item.LngRange)
	dti.SetInsertValue("addr_format", item.AddrFormat)
	dti.SetInsertValue("region_code", item.RegionCode)
	dti.SetInsertValue("country", item.Country)
	dti.SetInsertValue("wgs84_coord", item.WGS84Coord)
	dti.SetInsertValue("bd09_coord", item.BD09Coord)
	dti.SetInsertValue("insert_tick", time.Now().Unix())
	sql, values := dti.GenerateSQL()
	mysqlHelper := db.GetOpenapiMySQLHelper()
	_, err := mysqlHelper.Exec(sql, values...)
	if err != nil {
		log.Warnf("saveGeoAddrItem2Table error: %s, values: %v", err.Error(), values)
	}
	return err
}

//从数据库中加载所有数据
func loadGeoAddrData() {
	sid := int64(0)
	batchNum := 2000
	total := 0
	sql := "select id, geohash, lat_range, lng_range, addr_format, region_code, country, wgs84_coord, bd09_coord from geohash_address where id > ? order by id limit ?"
	mysqlHelper := db.GetOpenapiMySQLHelper()
	for {
		results, err := mysqlHelper.Query(sql, sid, batchNum)
		if err != nil {
			log.Warnf("loadGeoAddrData error: %s, sid: %d, batchNum: %d", err.Error(), sid, batchNum)
			time.Sleep(time.Second * 10)
			continue
		}
		for _, dt := range results {
			sid = mysqlHelper.GetColInt64(dt, "id")
			total += 1
			item := &GeoHashAddrItem{}
			item.Hash = mysqlHelper.GetColString(dt, "geohash")
			item.LatRange = mysqlHelper.GetColString(dt, "lat_range")
			item.LngRange = mysqlHelper.GetColString(dt, "lng_range")
			item.AddrFormat = mysqlHelper.GetColString(dt, "addr_format")
			item.RegionCode = mysqlHelper.GetColString(dt, "region_code")
			item.Country = mysqlHelper.GetColString(dt, "country")
			item.WGS84Coord = mysqlHelper.GetColString(dt, "wgs84_coord")
			item.BD09Coord = mysqlHelper.GetColString(dt, "bd09_coord")
			geoaddrData.Set(item.Hash, item)
		}
		if len(results) < batchNum {
			log.Infof("total load %d geohash data", total)
			break
		}
	}
}

func init() {
	geoaddrData = utility.NewSafeMap()
	go loadGeoAddrData()
}
